package com.dq.spring.core;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

public class ClassPathXmlApplicationContext implements ApplicationContext {
	private Map<String, Object> singeton = new HashMap<String, Object>();
	private List<ClassDefinition> beans = new ArrayList<ClassDefinition>();

	public ClassPathXmlApplicationContext(String path) {
		pareseXml(path);	//解析xml文件获取bean信息

		try {
			makeInstance();	//实例化这些需要IOC容器管理的类
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

	private void makeInstance() throws Exception {
		if(beans.isEmpty()) {
			return;
		}

		Class c = null;
		Method[] methods = null;
		Map<String, Method> map = null;
		String methodName = null;
		Object obj = null;
		Map<String, String> properties;
		Method md = null;
		String typeName = null;

		for(ClassDefinition cls : beans) {
			c = Class.forName(cls.getClassName()); 
			obj = c.newInstance(); //调用这个类的无参构造
			properties = cls.getPros(); 


			if (properties == null || properties. isEmpty()) {
				singeton.put(cls.getId(),obj);
				continue;
			}

			//获取这个类中的setter方法 -> 获取这个类的所有方法
			methods = c.getDeclaredMethods();
			map = new HashMap<String, Method>();
			for (Method method : methods) {
				methodName = method.getName() ;
				if (methodName.startsWith("set")) {
					map.put(methodName, method) ;
				}

				//然后找到对应属性的set方法后通过invoke()注值 -> 必须知道这个setter方法所需值的类型，即setter方法中第一个形参的类型
				for (Entry<String, String> entry : properties.entrySet()) {
					methodName = entry.getKey();
					methodName = "set" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);

					//获取这个方法
					md = map.get(methodName);
					if (md == null) {
						continue;
					}

					//取出第一个形参的参数
					typeName = md.getParameterTypes()[0].getSimpleName();
					if ("int".equals(typeName) || "Integer".equals(typeName)) {
						md.invoke (obj, Integer.parseInt(entry.getValue()));
					} else if ("float".equals(typeName) || "Float".equals(typeName)) {
						md.invoke(obj,Float.parseFloat(entry.getValue()));
					} else if ("double".equals(typeName) || "Double".equals(typeName)) {
						md.invoke(obj,Double.parseDouble(entry.getValue()));
					} else {
						md.invoke(obj, entry.getValue());
					}
				}			
			}
			singeton.put(cls.getId(), obj);
		}
	}

	//解析xml文件
	private void pareseXml(String path) {
		SAXReader read = new SAXReader();
		Document doc = null;

		try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(path)) {
			doc = read.read(is) ;
			XPath xpath = doc.createXPath ("//dq:bean") ;
			Map<String, String> nsmap = new HashMap<String, String>();
			nsmap.put ("dq", "http://www.springframework.org/schema/beans") ;
			xpath.setNamespaceURIs(nsmap); //设置命名空间信息

			List<Element> nodes = xpath.selectNodes(doc) ;

			if (nodes.isEmpty()) {
				return;
			}

			String id = null;
			String className = null;
			ClassDefinition bean = null;
			List<Element> properties = null;

			for (Element el : nodes) {
				id = el.attributeValue("id") ;
				className = el.attributeValue("class");

				bean = new ClassDefinition(id, className);

				//找bean的属性值
				properties = el.elements();
				if (properties != null && !properties.isEmpty()) {
					for (Element pro : properties) {
						bean.addPro(pro.attributeValue("name"), pro.attributeValue("value"));
					}
				}

				beans.add(bean) ;
			}

		} catch (DocumentException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public Object getBean(String id) {
		return singeton.getOrDefault(id, null);
	}

}
